/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>			                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./Renderer.h"
#include "../Objects/Ellipsoid.h"

Renderer::Renderer() {
	mAdaptiveStep = 0;
	mhWnd = cNull;
	mhMemDC = cNull;
	mhMemBmp = cNull;
	mhOldMemBmp = cNull;
	mWidth = mHeight = 0;
	mClear = true;
}

Renderer::Renderer(const Renderer& renderer) {
	mhWnd = renderer.mhWnd;
	mWidth = renderer.mWidth;
	mHeight = renderer.mHeight;

	HDC hdc = ::GetDC(mhWnd);
	::SelectObject(mhMemDC, mhOldMemBmp);
	::DeleteObject(mhMemBmp);
	::DeleteObject(mhMemDC);
	mhMemDC = ::CreateCompatibleDC(hdc);
	mhMemBmp = ::CreateCompatibleBitmap(hdc, mWidth, mHeight);
	mhOldMemBmp = (HBITMAP) ::SelectObject(mhMemDC, mhMemBmp);
	::ReleaseDC(mhWnd, hdc);
}

Renderer::~Renderer() {
	::SelectObject(mhMemDC, mhOldMemBmp);
	::DeleteObject(mhMemBmp);
	::DeleteObject(mhMemDC);
}

cVoid Renderer::Init(HWND windowHandle) {
	mhWnd = windowHandle;

	if (!mhMemDC) {
		::SelectObject(mhMemDC, mhOldMemBmp);
		::DeleteObject(mhMemBmp);
		::DeleteObject(mhMemDC);
	}

	cRect rect;

	HDC hdc = ::GetDC(mhWnd);
	mhMemDC = ::CreateCompatibleDC(hdc);
	::GetClientRect(mhWnd, &rect);
	mhMemBmp = ::CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
	mhOldMemBmp = (HBITMAP) ::SelectObject(mhMemDC, mhMemBmp);
	::ReleaseDC(mhWnd, hdc);
	mWidth = rect.right;
	mHeight = rect.bottom;

	Clear();
}

cVoid Renderer::Render() {
	HDC hdc = ::GetDC(mhWnd);
	cRect rect;

	::GetClientRect(mhWnd, &rect);
	::BitBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, mhMemDC, rect.right, rect.bottom, SRCCOPY);
	::ReleaseDC(mhWnd, hdc);
}

cVoid Renderer::Render(HDC hdc, cRect rect) {
	if (mAdaptiveStep != -1) {
		::BitBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, mhMemDC, rect.left, rect.top, SRCCOPY);
		Clear();
	}
}

cVoid Renderer::Refresh() {
	cRect rect;

	::GetClientRect(mhWnd, &rect);
	::InvalidateRect(mhWnd, &rect, false);
}

cVoid Renderer::Clear() {
	cRect rect;

	if (mClear && mAdaptiveStep != -1) {
		::SetRect(&rect, 0, 0, mWidth, mHeight);

		HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
		::FillRect(mhMemDC, &rect, brush);
		::DeleteObject(brush);
	} else {
		::SetRect(&rect, mWidth - 100, 0, mWidth, 100);

		HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
		::FillRect(mhMemDC, &rect, brush);
		::DeleteObject(brush);
	}
}

cVoid Renderer::Resize() {
	cRect rect;

	HDC hdc = ::GetDC(mhWnd);
	::GetClientRect(mhWnd, &rect);
	::SelectObject(mhMemDC, mhOldMemBmp);
	::DeleteObject(mhMemBmp);
	mhMemBmp = ::CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
	mhOldMemBmp = (HBITMAP) ::SelectObject(mhMemDC, mhMemBmp);
	::ReleaseDC(mhWnd, hdc);
	mWidth = rect.right;
	mHeight = rect.bottom;
	Clear();
}

cInt Renderer::GetWidth() {
	return mWidth;
}

cInt Renderer::GetHeight() {
	return mHeight;
}

cInt Renderer::GetAdaptiveStep() {
	return mAdaptiveStep;
}

cVoid Renderer::ResetAdaptiveStep() {
	mAdaptiveStep = 0;
}

cVoid Renderer::IncreaseAdaptive() {
	mAdaptiveStep++;
}

cVoid Renderer::StopAdaptive() {
	mAdaptiveStep = -1;
}

cVoid Renderer::SetPixel(cInt iX, cInt iY, cWinColor color) {
	if (iX < 0 || mWidth <= iX || iY < 0 || mHeight <= iY) {
		return;
	}
	::SetROP2(mhMemDC, R2_MERGEPEN);
	::SetPixel(mhMemDC, iX, iY, color);
}

cVoid Renderer::DrawRectangle(cInt beginPointX, cInt beginPointY, cInt xWidth, cInt yHeight, cWinColor color) {	
	cRect rect;

	rect.left = beginPointX;
	rect.top = beginPointY;
	rect.right = beginPointX + xWidth;
	rect.bottom = beginPointY + yHeight;
	HBRUSH hBrush = ::CreateSolidBrush(color);
	::FillRect(mhMemDC, &rect, hBrush);
	::DeleteObject(hBrush);
}

cVoid Renderer::DrawString(cInt iX, cInt iY, const cChar *text, cBool bBold, 
						   cBool bLeftAligned, cBool bTopAligned, cWinColor color) {
	HFONT hFont = ::CreateFont(14, 0, 0, 0, bBold ? FW_BOLD : 0, FALSE, FALSE, FALSE, ANSI_CHARSET,
							OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 0, DEFAULT_PITCH, L"Arial");
	HFONT hOldFont = (HFONT) ::SelectObject(mhMemDC, hFont);
	::SetBkMode(mhMemDC, TRANSPARENT);
	::SetTextColor(mhMemDC, color);
	::SetTextAlign(mhMemDC, (bTopAligned ? TA_TOP : TA_BOTTOM) | (bLeftAligned ? TA_LEFT : TA_RIGHT));
	::TextOutA(mhMemDC, iX, iY, text, (cInt) strlen(text));

	::SelectObject(mhMemDC, hOldFont);
	::DeleteObject(hFont);
}

cVoid Renderer::DrawRectangle(cInt beginPointX, cInt beginPointY, cInt xWidth, cInt yHeight, 
							  cWinColor color, cBool bFill) {
	if (bFill) {
		DrawRectangle(beginPointX, beginPointY, xWidth, yHeight, color);
	} else {
		DrawLine(beginPointX, beginPointY, beginPointX + xWidth, beginPointY, color);
		DrawLine(beginPointX, beginPointY + yHeight, beginPointX + xWidth, beginPointY + yHeight, color);
		DrawLine(beginPointX, beginPointY, beginPointX, beginPointY + yHeight, color);
		DrawLine(beginPointX + xWidth, beginPointY, beginPointX + xWidth, beginPointY + yHeight, color);
	}
}

cVoid Renderer::DrawROPLine(const Point4D& p1, const Point4D& p2, const cWinColor color) {
	DrawROPLine((cInt) p1.mX, (cInt) p1.mY, (cInt) p2.mX, (cInt) p2.mY, color);
}

cVoid Renderer::DrawROPLine(cInt beginPointX, cInt beginPointY, cInt endPointX, cInt endPointY,
							cWinColor color) {
	HPEN pen = ::CreatePen(PS_SOLID, 1, color);
	HPEN oldpen = (HPEN) ::SelectObject(mhMemDC, pen);

	::SetROP2(mhMemDC, R2_MERGEPEN);
	::MoveToEx(mhMemDC, beginPointX, beginPointY, cNull);
	::LineTo(mhMemDC, endPointX, endPointY);

	::SelectObject(mhMemDC, oldpen);
	::DeleteObject(pen);
}

cVoid Renderer::DrawLine(const Point4D& p1, const Point4D& p2, const cWinColor color) {
	DrawLine((cInt) p1.mX, (cInt) p1.mY, (cInt) p2.mX, (cInt) p2.mY, color);
}

cVoid Renderer::DrawLine(cInt beginPointX, cInt beginPointY, 
						 cInt endPointX, cInt endPointY, cWinColor color) {
	HPEN pen = ::CreatePen(PS_SOLID, 1, color);
	HPEN oldpen = (HPEN) ::SelectObject(mhMemDC, pen);

	::MoveToEx(mhMemDC, beginPointX, beginPointY, cNull);
	::LineTo(mhMemDC, endPointX, endPointY);

	::SelectObject(mhMemDC, oldpen);
	::DeleteObject(pen);
}

cVoid Renderer::DrawPoint(const Point4D& p1, const cWinColor cColor, cShort sMarked) {
	HPEN pen, oldpen;
	HBRUSH hOldBrush;
	cWinColor color = cColor;

	if (sMarked < 2) {
		color = RGB(GetRValue(cColor) >> 1, GetGValue(cColor) >> 1, GetBValue(cColor) >> 1);
	}

	::SetROP2(mhMemDC, R2_MERGEPEN);
	pen = ::CreatePen(PS_SOLID, 4, color);
	oldpen = (HPEN) ::SelectObject(mhMemDC, pen);
	hOldBrush = (HBRUSH) ::SelectObject(mhMemDC, ::GetStockObject(BLACK_BRUSH));

	::MoveToEx(mhMemDC, (cInt) p1.mX, (cInt) p1.mY, cNull);
	::LineTo(mhMemDC, (cInt) p1.mX, (cInt) p1.mY);

	::SelectObject(mhMemDC, oldpen);
	::DeleteObject(pen);

	pen = ::CreatePen(PS_SOLID, 1, color);
	oldpen = (HPEN) ::SelectObject(mhMemDC, pen);

	if (sMarked != 0) {
		::Ellipse(mhMemDC, (cInt) p1.mX - 5, (cInt) p1.mY - 5, (cInt) p1.mX + 6, (cInt) p1.mY + 6);
	}

	::SelectObject(mhMemDC, oldpen);
	::SelectObject(mhMemDC, hOldBrush);
	::DeleteObject(pen);
}

cVoid Renderer::SetClear(cBool bClear) {
	mClear = bClear;
}